package com.project.modules.redEnvelope;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.project.common.api.CommonPage;
import com.project.common.api.CommonResult;
import com.project.common.service.RedisService;
import com.project.modules.notice.model.TblMessage;
import com.project.modules.notice.service.AliyunSmsService;
import com.project.modules.notice.service.TblMessageService;
import com.project.modules.redEnvelope.model.TblRecord;
import com.project.modules.redEnvelope.model.TblRedEnvelope;
import com.project.modules.redEnvelope.model.TblSign;
import com.project.modules.redEnvelope.service.TblRecordService;
import com.project.modules.redEnvelope.service.TblRedEnvelopeService;
import com.project.modules.redEnvelope.service.TblSignService;
import com.project.modules.redEnvelope.utils.RedEnvelopeUtil;
import com.project.modules.redEnvelope.vo.RankListVo;
import com.project.modules.redEnvelope.vo.RedEnvelopeDetailVo;
import com.project.modules.redEnvelope.vo.RedEnvelopePageVo;
import com.project.modules.ums.model.UmsUser;
import com.project.modules.ums.service.UmsUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import static com.project.common.api.ResultCode.RED_PACKAGE_SIGN_REAPT;
import static com.project.modules.redEnvelope.constant.Constant.*;

/**
 * @author Josh-ZJUT
 * @date 2023/9/5 18:55
 * @email dujianghui537885@163.com
 */
@RequestMapping("/red")
@RestController
@Api(value = "RedController", tags = "【红包模块】")
@Slf4j
public class RedController {

    @Autowired
    private TblMessageService messageService;
    @Autowired
    private RedisService redisService;

    @Autowired
    private TblRedEnvelopeService redEnvelopeService;

    @Autowired
    private TblSignService signService;

    @Autowired
    private UmsUserService userService;

    @Autowired
    private TblRecordService recordService;

    @Autowired
    private AliyunSmsService aliyunSmsService;


    @ApiOperation("【企业】快速创建红包活动")
    @GetMapping("/create/quick")
    public CommonResult<Object> createQuickActivity() {
        // 检查余额是否够发
        final UmsUser user = userService.getByUsername("zjut");
        final BigDecimal accountBalance = user.getAccountBalance();
        final DateTime dateTime = DateUtil.offsetMinute(DateUtil.date(), 1);
        TblRedEnvelope redEnvelope = TblRedEnvelope.builder()
                .activityName("注意！红包雨来袭～")
                .picture("https://img0.baidu.com/it/u=1762633732,4264056624&fm=253&fmt=auto&app=138&f=JPEG?w=756&h=500")
                .content("中央气象台预计从" + dateTime.toString(TimeZone.getTimeZone("Asia/Shanghai")) + "开始，一股较强的红包雨将来袭，这也是今年下半年以来最强的红包雨")
                .signStartTime(DateUtil.date())
                .signEndTime(dateTime)
                .headCountLimit(10)
                .startTime(dateTime).totalAmount(new BigDecimal("100.00")).redCount(10).build();
        log.info(redEnvelope.getContent());
        if (redEnvelope.getTotalAmount().compareTo(accountBalance) > 0) {
            return CommonResult.failed("余额不足，请先充值");
        } else {
            // 扣钱
            user.setAccountBalance(user.getAccountBalance().subtract(redEnvelope.getTotalAmount()));
            userService.updateById(user);
        }

        // 拆红包
        BigDecimal[] splitRedEnvelopes = RedEnvelopeUtil.splitRedEnvelopeAlgorithm(redEnvelope.getTotalAmount(), redEnvelope.getRedCount());
        // 持久化
        redEnvelope.setSplitEnvelopes(RedEnvelopeUtil.bigDecimals2String(splitRedEnvelopes));
        if (redEnvelope.getEndTime() == null) {
            redEnvelope.setEndTime(DateUtil.offsetSecond(redEnvelope.getStartTime(), RED_ENVELOPE_DURING_SECONDS));
        }
        if (StrUtil.isEmpty(redEnvelope.getPicture())) {
            redEnvelope.setPicture("https://media.istockphoto.com/id/1448690922/photo/chinese-new-year-decorations-red-packet-red-envelope-with-clean-dark-background.jpg?s=2048x2048&w=is&k=20&c=nHKd7zPL12B4L0ClLFHaUSXWvp_TNTDdilTMD_iYXVg=");
        }
        redEnvelope.setCreateBy("zjut");
        boolean success = redEnvelopeService.save(redEnvelope);
        final Long redId = redEnvelope.getId();
        // 启动定时任务，在活动开始时发送通知
        redEnvelopeService.scheduleStartAndEnd(redId, redEnvelope.getStartTime(), redEnvelope.getEndTime());
        // 缓存
        redisService.lPushAll(RED_ENVELOPE_CREATE_KEY + redId, 60 * 60 * 24L, splitRedEnvelopes);
        return success ? CommonResult.success(redId) : CommonResult.failed();
    }

    @ApiOperation("【企业】创建红包活动")
    @PostMapping("/create")
    public CommonResult<Object> createActivity(@RequestBody TblRedEnvelope redEnvelope, Principal principal) {
        // 检查余额是否够发
        final UmsUser user = userService.getByUsername(principal.getName());
        final BigDecimal accountBalance = user.getAccountBalance();
        if (redEnvelope.getTotalAmount().compareTo(accountBalance) > 0) {
            return CommonResult.failed("余额不足，请先充值");
        } else {
            // 扣钱
            user.setAccountBalance(user.getAccountBalance().subtract(redEnvelope.getTotalAmount()));
            userService.updateById(user);
        }

        // 拆红包
        BigDecimal[] splitRedEnvelopes = RedEnvelopeUtil.splitRedEnvelopeAlgorithm(redEnvelope.getTotalAmount(), redEnvelope.getRedCount());
        // 持久化
        redEnvelope.setSplitEnvelopes(RedEnvelopeUtil.bigDecimals2String(splitRedEnvelopes));
        if (redEnvelope.getEndTime() == null) {
            redEnvelope.setEndTime(DateUtil.offsetSecond(redEnvelope.getStartTime(), RED_ENVELOPE_DURING_SECONDS));
        }
        if (StrUtil.isEmpty(redEnvelope.getPicture())) {
            redEnvelope.setPicture("https://media.istockphoto.com/id/1448690922/photo/chinese-new-year-decorations-red-packet-red-envelope-with-clean-dark-background.jpg?s=2048x2048&w=is&k=20&c=nHKd7zPL12B4L0ClLFHaUSXWvp_TNTDdilTMD_iYXVg=");
        }
        boolean success = redEnvelopeService.save(redEnvelope);
        final Long redId = redEnvelope.getId();
        // 启动定时任务，在活动开始时发送通知
        redEnvelopeService.scheduleStartAndEnd(redId, redEnvelope.getStartTime(), redEnvelope.getEndTime());
        // 缓存
        redisService.lPushAll(RED_ENVELOPE_CREATE_KEY + redId, 60 * 60 * 24L, splitRedEnvelopes);
        return success ? CommonResult.success(redId) : CommonResult.failed();
    }

    @ApiOperation("【用户】查询所有红包活动")
    @GetMapping(value = "/page")
    public CommonResult<CommonPage<RedEnvelopePageVo>> getPage(@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
                                                               @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
        Page<TblRedEnvelope> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<TblRedEnvelope> wrapper = new LambdaQueryWrapper<TblRedEnvelope>()
//                .notBetween(TblRedEnvelope::getStartTime,"2023-09-13 15:00:00","2023-09-14 22:20:00")
                .orderByDesc(TblRedEnvelope::getStartTime);
        final Page<TblRedEnvelope> redEnvelopePage = redEnvelopeService.page(page, wrapper);
        final IPage<RedEnvelopePageVo> convert = redEnvelopePage.convert(redEnvelope -> {
            // 进行中
            int status = 0;
            final Long redId = redEnvelope.getId();
            // 获取报名人数
            long curCount = 0;
            final Object curCountRedis = redisService.get(RED_ENVELOPE_CREATE_COUNT + redId);
            if (curCountRedis != null) {
                curCount = ((Integer) curCountRedis).longValue();
            }
//            log.info("当前报名人数:" + curCount);
//            log.info("红包限制人数:" + redEnvelope.getHeadCountLimit());
            if (curCount >= redEnvelope.getHeadCountLimit()) {
                // 已满
                status = 1;
            } else if (DateUtil.compare(DateUtil.date(), redEnvelope.getSignEndTime()) >= 0) {
                // 已结束
                status = 2;
            }
            final UmsUser creator = userService.getByUsername(redEnvelope.getCreateBy());
            String nickName = null, icon = null;
            if (creator != null) {
                nickName = creator.getNickName();
                icon = creator.getIcon();
            }
            // 更新状态
            if (redEnvelope.getStatus() == null || !redEnvelope.getStatus().equals(status)) {
                redEnvelope.setStatus(status);
                redEnvelopeService.updateById(redEnvelope);
            }
            return RedEnvelopePageVo.builder()
                    .redId(redId)
                    .activityName(redEnvelope.getActivityName())
                    .totalAmount(redEnvelope.getTotalAmount())
                    .status(status)
                    .content(redEnvelope.getContent())
                    .startTime(redEnvelope.getStartTime())
                    .nickName(nickName)
                    .icon(icon)
                    .build();
        });
        return CommonResult.success(CommonPage.restPage(convert));
    }

    @ApiOperation("【企业】查询自己发起的红包活动")
    @GetMapping(value = "/myCreate")
    public CommonResult<CommonPage<TblRedEnvelope>> getMyCreatePage(@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
                                                                    @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
                                                                    Principal principal) {
        Page<TblRedEnvelope> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<TblRedEnvelope> wrapper = new LambdaQueryWrapper<TblRedEnvelope>()
                .eq(TblRedEnvelope::getCreateBy, principal.getName()).orderByDesc(TblRedEnvelope::getStartTime);
        final Page<TblRedEnvelope> redEnvelopePage = redEnvelopeService.page(page, wrapper);
        return CommonResult.success(CommonPage.restPage(redEnvelopePage));
    }

    @ApiOperation("【用户】详细查询某个红包")
    @GetMapping(value = "/detail/{redId}")
    public CommonResult<RedEnvelopeDetailVo> getById(@PathVariable Long redId, Principal principal) {
        final Long userId = userService.getByUsername(principal.getName()).getId();

        TblRedEnvelope redEnvelope = redEnvelopeService.getById(redId);
        RedEnvelopeDetailVo vo = new RedEnvelopeDetailVo();
        BeanUtils.copyProperties(redEnvelope, vo);
        vo.setNickName(userService.getByUsername(redEnvelope.getCreateBy()).getNickName());
        final long count = signService.count(new LambdaQueryWrapper<TblSign>()
                .eq(TblSign::getUserId, userId)
                .eq(TblSign::getRedId, redId));
        vo.setSignStatus(count);
        return CommonResult.success(vo);
    }

    @ApiOperation("【用户】报名抢红包")
    @PostMapping("/sign/{redId}")
    public CommonResult<Object> sign(@PathVariable Long redId, Principal principal) {
        UmsUser user = userService.getByUsername(principal.getName());
        long userId = user.getId();

        final TblRedEnvelope envelope = redEnvelopeService.getById(redId);
        // 判断组内红包是否有权限抢
        if (envelope.getSameGroup().equals(1)) {
            final String creatorBelongTo = userService.getByUsername(envelope.getCreateBy()).getBelongTo();
            if (!user.getBelongTo().equals(creatorBelongTo)) {
                return CommonResult.failed("这是别的部门的红包");
            }
        }

        // 判断人数是否已满
        final Object o = redisService.get(RED_ENVELOPE_CREATE_COUNT + redId);
        long count = 0;
        if (o == null) {
            // 缓存没有，查数据库
            count = signService.count(new LambdaQueryWrapper<TblSign>().eq(TblSign::getRedId, redId));
        } else {
            // 返回缓存
            count = ((Integer) o).longValue();
        }
        if (envelope.getHeadCountLimit() <= count) {
            return CommonResult.failed("人数已满");
        }
        // 报名
        TblSign sign = new TblSign();
        sign.setUserId(userId);
        sign.setRedId(redId);
        boolean success;
        try {
            success = signService.save(sign);
        } catch (DuplicateKeyException e) {
            return CommonResult.failed(RED_PACKAGE_SIGN_REAPT);
        }
        // 报名人数放缓存
        redisService.incr(RED_ENVELOPE_CREATE_COUNT + redId, 1L);
        log.info("当前报名人数：" + count);
        // 预约一个通知：红包雨在十分钟后开始
        final DateTime dateTime = DateUtil.offsetMinute(envelope.getStartTime(), -10);
        final String activityName = redEnvelopeService.getById(redId).getActivityName();
        String msg = "您报名的【" + activityName + "】活动将于10分钟后开始，赶紧上线吧！";
        // 发送邮件通知
        TblMessage message = TblMessage.builder()
                .redId(redId)
                .content(msg)
                .receiveUserId(userId)
                .sendTime(dateTime)
                .sendUserId(userService.getByUsername(envelope.getCreateBy()).getId())
                .isAuto(1)
                .method(1)
                .build();
        messageService.save(message);
        // 发送短信通知
        TblMessage emailMessage = new TblMessage();
        BeanUtils.copyProperties(message, emailMessage);
        emailMessage.setMethod(2);
        emailMessage.setId(null);
        messageService.save(emailMessage);
        return success ? CommonResult.success("您是第" + count + "个报名的人") : CommonResult.failed();
    }

    @ApiOperation("【用户】提交抢红包分数（多次调用）")
    @PostMapping(value = "/submitScore")
    public CommonResult<Object> submitScore(@RequestParam(value = "redId") Long redId,
                                            @RequestParam(value = "score") Integer score,
                                            Principal principal) {
        long userId = userService.getByUsername(principal.getName()).getId();
        final Boolean success = redisService.hSet(RED_ENVELOPE_SCORE + redId, userId + "", score, RED_ENVELOPE_DURING_SECONDS);
        return success ? CommonResult.success() : CommonResult.failed();
    }


//    @ApiOperation("【企业】结算红包（定时任务执行了就不需要调用）")
//    @GetMapping(value = "/settle/{redId}")
//    public CommonResult<Object> settle(@PathVariable Long redId) {
//        redEnvelopeService.settleRedEnvelope(redId);
//        return CommonResult.success();
//    }

    @ApiOperation("【用户】根据得分排名分配红包")
    @GetMapping(value = "/rob/{redId}")
    public CommonResult<BigDecimal> rob(@PathVariable Long redId,
                                        Principal principal) throws ExecutionException, InterruptedException {
        long userId = userService.getByUsername(principal.getName()).getId();

        CompletableFuture<BigDecimal> completableFuture = new CompletableFuture<>();

        // 在异步任务中等待锁的释放
        CompletableFuture.runAsync(() -> {
            redisService.waitForLockRelease(RED_ENVELOPE_LOCK + redId);
            BigDecimal money = (BigDecimal) redisService.hGet(RED_ENVELOPE_CONSUME_KEY + redId, userId + "");
            log.info("抢到了" + money);
            completableFuture.complete(money);
        });
        return CommonResult.success(completableFuture.get());
    }

    @ApiOperation("【企业】查看红包领取情况")
    @GetMapping(value = "/log/{redId}")
    public CommonResult<Map<Long, BigDecimal>> logByRedId(@PathVariable Long redId) {
        final Map<Object, Object> objectMap = redisService.hGetAll(RED_ENVELOPE_CONSUME_KEY + redId);
        Map<Long, BigDecimal> map;
        if (objectMap == null) {
            // 查询数据库
            map = recordService.lambdaQuery()
                    .eq(TblRecord::getReceiveAmount, redId)
                    .select(TblRecord::getReceviceUserId, TblRecord::getReceiveAmount)
                    .list()
                    .stream()
                    .collect(Collectors.toMap(
                            // key: userId
                            TblRecord::getReceviceUserId,
                            // value: money
                            TblRecord::getReceiveAmount
                    ));
        } else {
            // 查询缓存
            map = RedEnvelopeUtil.transferUserMoney(objectMap);
        }
        return CommonResult.success(map);
    }

    @ApiOperation("【管理员】查看红包领取情况")
    @GetMapping(value = "/log")
    public CommonResult<List<TblRecord>> logAll() {
        return CommonResult.success(recordService.lambdaQuery()
                .select(TblRecord::getReceviceUserId, TblRecord::getReceiveAmount)
                .list());
    }

    @ApiOperation("【所有人】查看红包排行榜")
    @GetMapping(value = "/rank/{redId}")
    public CommonResult<List<RankListVo>> rankByRedId(@PathVariable Long redId) {
        final List<RankListVo> collect = recordService.list(new LambdaQueryWrapper<TblRecord>()
                .eq(redId != null, TblRecord::getRedId, redId)
                .orderByDesc(TblRecord::getReceiveAmount)
                .last("LIMIT 10")
        ).stream().map(record -> {
            final UmsUser user = userService.getById(record.getReceviceUserId());
            String nickname = "", icon = "";
            if (user != null) {
                nickname = user.getNickName();
                icon = user.getIcon();
            }
            return RankListVo.builder()
                    .nickName(nickname)
                    .icon(icon)
                    .receiveAmount(record.getReceiveAmount()).build();
        }).collect(Collectors.toList());
        return CommonResult.success(collect);
    }

    @ApiOperation("【所有人】充值")
    @GetMapping(value = "/income/{money}")
    public CommonResult<Object> income(@PathVariable BigDecimal money, Principal principal) {
        UmsUser user = userService.getByUsername(principal.getName());
        BigDecimal sum = user.getAccountBalance().add(money).setScale(2, RoundingMode.HALF_UP);
        if (sum.intValue() > Integer.MAX_VALUE) {
            return CommonResult.failed("差不多得了");
        }
        user.setAccountBalance(sum);
        boolean success = userService.updateById(user);
        return success ? CommonResult.success(user.getAccountBalance()) : CommonResult.failed();
    }

    @ApiOperation("【所有人】提现")
    @GetMapping(value = "/outcome/{money}")
    public CommonResult<Object> outcome(@PathVariable BigDecimal money, Principal principal) {
        UmsUser user = userService.getByUsername(principal.getName());
        BigDecimal subtract = user.getAccountBalance().subtract(money).setScale(2, RoundingMode.HALF_UP);
        if (subtract.intValue() < 0) {
            return CommonResult.failed("你没那么多钱");
        }
        user.setAccountBalance(subtract);
        boolean success = userService.updateById(user);
        return success ? CommonResult.success(user.getAccountBalance()) : CommonResult.failed();
    }

    @ApiOperation("【所有人】查询收到的最新消息")
    @GetMapping("/news")
    public CommonResult<String> getNews(Principal principal) {
        long userId = userService.getByUsername(principal.getName()).getId();
        final List<TblMessage> messages = messageService.list(new LambdaQueryWrapper<TblMessage>()
                .eq(TblMessage::getReceiveUserId, userId)
                .orderByDesc(TblMessage::getCreateTime)
                .last("LIMIT 1")
        );
        if (CollectionUtil.isNotEmpty(messages)) {
            return CommonResult.success(messages.get(0).getContent());
        }

        return CommonResult.success("您还有余额未使用，来商城看看吧~");
    }


//    @ApiOperation("【测试】短信")
//    @GetMapping("/send")
//    public CommonResult<Boolean> send(String phone) throws Exception {
//        return CommonResult.success(aliyunSmsService.sendCode(phone, RandomUtil.randomNumbers(4)));
//    }
}
